#include "FUSE_Functions.h"

int buscarArchivo(char*rutaArchivo,datosArchivo*data){

	fat32_directory *directorio,*ptr=	NULL;
	char*vecAux;
	directorio=malloc(sizeof(fat32_directory));
	int pos=0,tamanioPath=0,posAnt=0,i;
	directorio->tipo=0;
	
	for(i=0;rutaArchivo[i]!='.'&&i<strlen(rutaArchivo);i++);

	if(i<strlen(rutaArchivo))
		memcpy((rutaArchivo+i)," ",1);

	tamanioPath=strlen(rutaArchivo);

	pos=strcspn(rutaArchivo,"/");
	posAnt=pos+1;

	leerDirectorio(directorio,FAT32.numClusterDirRoot);

	pos=strcspn(rutaArchivo+posAnt,"/");

	vecAux=malloc(sizeof(char)*pos);
	strncpy(vecAux,rutaArchivo+posAnt,pos);
    pos+=posAnt;
	
    if(directorio->tipo==0)
    	return 3;

    ptr=directorio;

	while(ptr!=NULL&&(strncmp(ptr->nombre,vecAux,pos-1))!=0)
		ptr=ptr->sig;

	free(vecAux);

	if(ptr==NULL)
		return 1;

	else{
			while(pos<tamanioPath){
				posAnt=pos+1;

				if(ptr->tipo!=DIRECTORIO)
					return 1; //no puede haber un dir en un archivo

				free(directorio);
				directorio=malloc(sizeof(fat32_directory));
				directorio->tipo=0;
				leerDirectorio(directorio,ptr->numClusterInicial);

				pos=strcspn(rutaArchivo+posAnt,"/");

				if(pos>tamanioPath)
					return 1; //falla el parseo
				vecAux=malloc(pos);
				strncpy(vecAux,rutaArchivo+posAnt,pos);
				
				if(directorio->tipo==0)
					return 3;
				ptr=NULL;
				ptr=directorio;

				while(ptr!=NULL&&(0!=strncmp(ptr->nombre,vecAux,pos)))
					ptr=ptr->sig;

				if(ptr==NULL)
					return 1;

				pos+=posAnt;
				free(vecAux);
			}
			if(ptr->tipo==0)
				return 3;

			data->numCluster=ptr->numClusterInicial;
			data->tipo=ptr->tipo;
			data->tamanioArchivo=ptr->tamanio;

			return 0;
	}

}


/*____________________________________________________*/

int leerDatos(datosArchivo*data,char*buffer,int offset,int size){

	int indice,secXcluster,offsetCluster;
	static listaClusters *cadenaClusters=NULL;
	static listaClusters*ptr;int off=0;
	cadenaClusters=malloc(sizeof(listaClusters));
	cadenaClusters->numCluster=0;
	ptr=data->lista;
	offsetCluster=0;
	static int clusters=0;
	if(fmod (offset,FAT32.tamanioSector) ==0){

		clusters=(offset/FAT32.tamanioSector)/FAT32.sectoresPorCluster;
			while(clusters>1){
				ptr=ptr->sig;
				clusters--;
			}

			if((fmod(offset,8)*8)!=0)
				offsetCluster+=(fmod(offset,8)*8);
		}

		else{
			off=offset;
			while(off>0)
			off-=FAT32.tamanioSector;
			offsetCluster++;
		}

	if(clusters>0)
		ptr=ptr->sig;

	if(tamanioCache!=0){
		for(indice=0,secXcluster=0;indice<size;indice+=FAT32.tamanioSector*2,secXcluster+=2){
					if(secXcluster>=FAT32.sectoresPorCluster){
						ptr=ptr->sig;
						secXcluster=0;
					}
					int pos=buscarEnCache(data->cacheArchivo, (calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2);
					if(pos>=0)
						memcpy(data->cacheArchivo.contCache[pos].contenido,buffer+indice,FAT32.tamanioSector*2);
					else{
						realizar_pedido_lectura((calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2,buffer+indice);
						if(offset<tamanioCache*1024)
							 agregarEnCache(data->cacheArchivo,buffer+indice,(calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2);
					}
					offsetCluster=0;
				}


	}
	else{
		for(indice=0,secXcluster=0;indice<size;indice+=FAT32.tamanioSector*2,secXcluster+=2){
			if(secXcluster>=FAT32.sectoresPorCluster){
				ptr=ptr->sig;
				secXcluster=0;
			}

			realizar_pedido_lectura((calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2,buffer+indice);

			offsetCluster=0;
		}
	}
		return 0;

}

/*____________________________________________________*/

int escribirArchivo(char *rutaArchivo,datosArchivo*data,const char*buffer,int offset,int size){

	int indice,secXcluster,offsetCluster,cantClustersFaltantes;
	listaClusters *cadenaClusters=NULL;
	static listaClusters*ptr;
	cadenaClusters=malloc(sizeof(listaClusters));
	cadenaClusters->numCluster=0;
	offsetCluster=0;
	ptr=data->lista;
	int clusters=0;
	int off=0;

	if(fmod (offset,FAT32.tamanioSector) ==0){
		clusters=(offset/FAT32.tamanioSector)/FAT32.sectoresPorCluster;
			while(clusters>1){
				ptr=ptr->sig;
				clusters--;
			}
			if((fmod(offset,8)*8)!=0)
						offsetCluster+=(fmod(offset,8)*8);
		}
		else{
			off=offset;
			while(off>0)
			off-=FAT32.tamanioSector;
			offsetCluster++;
		}

	cantClustersFaltantes=calcularTamanioNuevo(offsetCluster,ptr->sig,size);
	if(data->tamanioArchivo==0)
		cantClustersFaltantes=0;
	if(cantClustersFaltantes>0){
		down(mutexFAT);
		if(numClustersLibres<cantClustersFaltantes){
			up(mutexFAT);
			return (-ENOSPC);
		}
		up(mutexFAT);
		int vectorLibres[cantClustersFaltantes];
		if(buscar_Clusters_libres(cantClustersFaltantes,vectorLibres)!=0)
			return (-EIO);


		else{
			while(ptr->sig!=NULL)
				ptr=ptr->sig;
			if(cantClustersFaltantes>1){
				grabar_FAT(ptr->numCluster,vectorLibres[0]);
				grabar_FAT(vectorLibres[0],vectorLibres[1]);
				agregarCluster(&(vectorLibres[0]),ptr);
				for(indice=1;indice<cantClustersFaltantes-1;indice++){
					agregarCluster(&(vectorLibres[indice]),ptr);
					grabar_FAT(vectorLibres[indice],vectorLibres[indice+1]);
				}
				agregarCluster(&(vectorLibres[indice]),ptr);
				grabar_FAT(vectorLibres[indice],0xFFFFFFF);
			}
			else{
				grabar_FAT(ptr->numCluster,vectorLibres[0]);
				grabar_FAT(vectorLibres[0],0xFFFFFFF);
				agregarCluster(&(vectorLibres[0]),ptr);
			}
		}
	}
	if(clusters>0&&ptr->sig!=NULL)
		ptr=ptr->sig;
	if(tamanioCache>0){
		for(indice=0,secXcluster=0;indice<size;indice+=FAT32.tamanioSector*2,secXcluster+=2){
			if(secXcluster>=FAT32.sectoresPorCluster){
				ptr=ptr->sig;
				secXcluster=0;
			}
			if((size-indice)>=1024)
				agregarEnCache(data->cacheArchivo,buffer+indice,(calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2);
			else{
				char miniBuff[1024];
				memset(miniBuff,0,1024);
				memcpy(miniBuff,buffer+indice,size-indice);
				agregarEnCache(data->cacheArchivo,miniBuff,(calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2);
			}

			offsetCluster=0;
		}
	}
	else{
		for(indice=0,secXcluster=0;indice<size;indice+=FAT32.tamanioSector*2,secXcluster+=2){
					if(secXcluster>=FAT32.sectoresPorCluster){
						ptr=ptr->sig;
						secXcluster=0;
					}
					if((size-indice)>=1024)
						realizar_pedido_escritura((calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2,buffer+indice);
					else{
						char miniBuff[1024];
						memset(miniBuff,0,1024);
						memcpy(miniBuff,buffer+indice,size-indice);
						realizar_pedido_escritura((calcularPrimerSectorDelCluster(ptr->numCluster)+offsetCluster+secXcluster)/2,miniBuff);
					}

					offsetCluster=0;
				}
	}
		if(((size>data->tamanioArchivo)&&(offset!=0))||((offset!=0)&&offset>=data->tamanioArchivo)||((size<data->tamanioArchivo)&&(offset!=0))){
			grabarEntradaArchivo(rutaArchivo,size+data->tamanioArchivo+(data->tamanioArchivo-offset));
			data->tamanioArchivo=size+data->tamanioArchivo+(data->tamanioArchivo-offset);
		}
		else{
			grabarEntradaArchivo(rutaArchivo,size);
			data->tamanioArchivo=size;
		}
		return 0;
	}

/*____________________________________________________*/

 int calcularTamanioNuevo(int desplazamientoCluster,listaClusters*cadenaClusters,int size){

	int indice,resultado;
	listaClusters*ptr=NULL;
	ptr=cadenaClusters;
	for(indice=0;ptr!=NULL;indice++)
		ptr=ptr->sig;
	resultado = (indice * FAT32.sectoresPorCluster)- desplazamientoCluster;
	int resul=0;
	resul=resultado;
	resultado= (resultado*FAT32.tamanioSector )- size;

	if(resultado>0)
		return (-EINVAL);
	else{
		resul= size- (resul*FAT32.tamanioSector);
		if((fmod(resul,FAT32.tamanioSector)!=0)||(size<(FAT32.tamanioSector*FAT32.sectoresPorCluster)))
			return ((((resul /FAT32.tamanioSector))/FAT32.sectoresPorCluster)+1);
		else
			return ((((resul /FAT32.tamanioSector))/FAT32.sectoresPorCluster));
	}
}

 /*____________________________________________________*/

 int grabarEntradaArchivo(char*rutaArchivo,int nuevoTamanio){

 	fat32_directory* directorio,*ptr=NULL;
 	char*vecAux;
 	int pos,i,tamanioPath,posAnt;
 	directorio=malloc(sizeof(fat32_directory));
 	directorio->tipo=0;

	for(i=0;rutaArchivo[i]!='.'&&i<strlen(rutaArchivo);i++);
	if(i<strlen(rutaArchivo))
		memcpy((rutaArchivo+i)," ",1);
 	tamanioPath=strlen(rutaArchivo);

 	pos=strcspn(rutaArchivo,"/");
 	posAnt=pos+1;
 	leerDirectorio(directorio,FAT32.numClusterDirRoot);
 	pos=strcspn(rutaArchivo+posAnt,"/");

 	vecAux=malloc(sizeof(char)*pos);
 	strncpy(vecAux,rutaArchivo+posAnt,pos);
    pos+=posAnt;
 	ptr=directorio;
 	if(directorio->tipo==0)
 		return 1;

 	while(ptr!=NULL&&(strncmp(ptr->nombre,vecAux,pos-1)!=0))
 		ptr=ptr->sig;

 	free(vecAux);

 	if(ptr==NULL)
 		return -ENOENT;

 	else{
 			while(pos<tamanioPath){
 				posAnt=pos+1;

 				if(ptr->tipo!=DIRECTORIO)
 					return -EFAULT; //no puede haber un dir en un archivo
 				free(directorio);
 				directorio=malloc(sizeof(fat32_directory));
 				directorio->tipo=0;
 				leerDirectorio(directorio,ptr->numClusterInicial);

 				pos=strcspn(rutaArchivo+posAnt,"/");
 				if(pos>tamanioPath)
 					return -ESPIPE; //falla el parseo
 				vecAux=malloc(sizeof(char)*pos);
 				strncpy(vecAux,rutaArchivo+posAnt,pos);

 				ptr=directorio;
 				if(directorio->tipo==0)
 					return 1;
 				while(ptr!=NULL&&(0!=strncmp(ptr->nombre,vecAux,pos)))
 					ptr=ptr->sig;

 				if(ptr==NULL)
 					return 1;

 				pos+=posAnt;
 				free(vecAux);
 			}
 			char bufferEscritura[1024];

 			realizar_pedido_lectura(ptr->numSectorEntrada/2,bufferEscritura);
 			if(ptr->LongFN)
 				ptr->offsetEntrada+=32;
 			if(nuevoTamanio>0xFF)
 				nuevoTamanio=(swap_uint32(nuevoTamanio));
 			memcpy(bufferEscritura+ptr->offsetEntrada+0x1C,&nuevoTamanio,sizeof(uint32_t));
 			realizar_pedido_escritura(ptr->numSectorEntrada/2,bufferEscritura);
 	}

 	return 0;
 }

 /*____________________________________________________*/

 int crearArchivoDirectorio(char* rutaArchivo,datosArchivo*data,mode_t modoApertura,int creacion){

	 int offset=0;
	 int sectorIni=0;
	 int numCluster=0;
	 int indice,i;
	 datosArchivo* dataLocal=malloc(sizeof(datosArchivo));

	 for(indice=strlen(rutaArchivo);rutaArchivo[indice]!='/';indice--);
	 	 if(indice==0)
	 		 indice++;
	 char *rutaAnt=malloc(indice+1);

	 memcpy(rutaAnt,rutaArchivo,indice);

	 rutaAnt[indice]='\0';

	 if(!strcmp(rutaAnt,"/")){
		 dataLocal->numCluster=2;
		 buscarEntradaLibre(2,&sectorIni,&offset);
	 }

	 else{
		 int resultado=0;
		 resultado=buscarArchivo(rutaAnt,dataLocal);

		 if(resultado!=0){
	 		crearArchivoDirectorio(rutaAnt,dataLocal,modoApertura,1);
	 		buscarArchivo(rutaAnt,dataLocal);
		 }
		buscarEntradaLibre(dataLocal->numCluster,&sectorIni,&offset);
		free(rutaAnt);
	 }

	 if(creacion==1){
		 down(mutexFAT);
		 if(numClustersLibres>=1){
			 up(mutexFAT);

			 int rIO= buscar_Clusters_libres(1,&numCluster);

			 if(rIO!=0){
				 return -EIO;
			 }
			 else{
				 data->numCluster=numCluster;
				 grabar_FAT(data->numCluster,0xFFFFFFF);
			 }
		 }
		 else{
			 up(mutexFAT);
			 return -ENOSPC;
		 }

	 }

	 indice=strlen(rutaArchivo);

	 for(i=0;rutaArchivo[indice]!='/';indice--,i++);
	 char nombre[13];
	 memset(nombre,0,13);

	 memcpy(nombre,rutaArchivo+(strlen(rutaArchivo)-(i-1)),i-1);
	 for(indice=0;nombre[indice]!='.'&&indice<strlen(nombre);indice++);
	 	 if(indice<strlen(nombre))
	 		 memcpy(nombre+indice," ",1);
	 escribirEntradaDirectorio(sectorIni,offset,nombre,data,dataLocal->numCluster);

	return 0;
}

 /*____________________________________________________*/

 int escribirEntradaDirectorio(int sector,int offset,char*nombreArchivo,datosArchivo*data,int pCluster){
	 char buffer[1024];
	 realizar_pedido_lectura(sector/2,buffer);
	 uint16_t vector[26];
	 memset(vector,0,26);
	 int tamanio=0;
	 unicode_utf8_to_utf16_inbuffer(nombreArchivo,strlen(nombreArchivo),vector,&tamanio);
	 int arch=0x20;
	 int dir=0x10;
	 memset(buffer+offset,0,64);
	 memcpy(buffer+offset,"1",1);
	 if(tamanio>5){
		memset(buffer+offset+1,0,sizeof(uint16_t)*5);
		memcpy(buffer+offset+1,vector,sizeof(uint16_t)*5);
		if((tamanio-5)>6){
			memset(buffer+offset+0x0E,0,sizeof(uint16_t)*6);
			memcpy(buffer+offset+0x0E,vector+5,sizeof(uint16_t)*6);
			memset(buffer+offset+0x1C,0,sizeof(uint16_t)*2);
			memcpy(buffer+offset+0x1C,vector+5+6,sizeof(uint16_t)*2);
		}
		else{
			memset(buffer+offset+0x0E,0,sizeof(uint16_t)*(tamanio-5));
			memcpy(buffer+offset+0x0E,vector+5,sizeof(uint16_t)*(tamanio-5));
		}
	 }
	 else{
		 memset(buffer+offset+1,0,sizeof(uint16_t)*tamanio);
		 memcpy(buffer+offset+1,vector,sizeof(uint16_t)*tamanio);
	 }
	 int lfn = 0x0F;
	 memcpy(buffer+offset+11,&lfn,1);
	 if(strlen(nombreArchivo)>11){
		 memset(buffer+offset+32,0,11);
		 memcpy(buffer+offset+32,nombreArchivo,7);
		 memcpy(buffer+offset+32+7,"~",1);
		 int i;
		 for(i=0;i<strlen(nombreArchivo)&& nombreArchivo[i]!=' ';i++);
			if(i<strlen(nombreArchivo))
				memcpy(buffer+offset+32+8,nombreArchivo+(strlen(nombreArchivo)-3),3);
	 }
	 else{
		  int i;
		  for(i=0;i<strlen(nombreArchivo)&& nombreArchivo[i]!=' ';i++);
		  if(i<strlen(nombreArchivo)){
			  memcpy(buffer+offset+32,nombreArchivo,strlen(nombreArchivo)-3);
			  memcpy(buffer+offset+32+8,nombreArchivo+(strlen(nombreArchivo)-3),3);
		  }
		  else
			  memcpy(buffer+offset+32,nombreArchivo,strlen(nombreArchivo));
	 }


	 if(data->tipo==DIRECTORIO){
		memcpy(buffer+offset+32+11,&(dir),1);
		int sectorDir= calcularPrimerSectorDelCluster(data->numCluster);
		char bufferDir[1024];
		realizar_pedido_lectura(sectorDir/2,bufferDir);
		int punt=0x2E;
		memset(bufferDir,0,64);
		memcpy(bufferDir,&(punt),1);
		memcpy(bufferDir+11,&(dir),1);
		memcpy(bufferDir+32,&(punt),1);
		memcpy(bufferDir+32+1,&(punt),1);
		memcpy(bufferDir+11+32,&(dir),1);
		char vecAux[4];
		uint32_t num=pCluster;
		memset(vecAux,0,4);

		 if(pCluster>0xFF){
		 	num=(swap_uint32(num));
		    memcpy(vecAux,&num,sizeof(uint32_t));
		 	memcpy(bufferDir+32+0x1A,vecAux,sizeof(uint16_t));
		 	memcpy(bufferDir+32+0x14,vecAux+2,sizeof(uint16_t));
		 }
		 else{
		 	memcpy(vecAux,&num,sizeof(uint32_t));
		 	memcpy(bufferDir+32+0x1A,vecAux,sizeof(uint16_t));
		 	memcpy(bufferDir+32+0x14,vecAux+2,sizeof(uint16_t));
		 }

		if(data->numCluster>0xFF){
				 memset(vecAux,0,4);
				 uint32_t nuevoTamanio=(swap_uint32(data->numCluster));
				 memcpy(vecAux,&nuevoTamanio,sizeof(uint32_t));
				 memcpy(bufferDir+0x1A,vecAux,sizeof(uint16_t));
				 memcpy(bufferDir+0x14,vecAux+2,sizeof(uint16_t));
		}
		else{
			memcpy(vecAux,&data->numCluster,sizeof(uint32_t));
			memcpy(bufferDir+0x1A,vecAux,sizeof(uint16_t));
			memcpy(bufferDir+0x14,vecAux+2,sizeof(uint16_t));
		}

		realizar_pedido_escritura(sectorDir/2,bufferDir);
	 }
	 else
		 memcpy(buffer+offset+32+11,&(arch),1);

	 if(data->tamanioArchivo>0xFF){
		 uint32_t nuevoTamanio=(swap_uint32(data->tamanioArchivo));
	 	 memcpy(buffer+offset+32+0x1C,&nuevoTamanio,sizeof(uint32_t));
	 }
	 else
		 memcpy(buffer+offset+32+0x1C,&(data->tamanioArchivo),sizeof(uint32_t));
	 char vecAux[4];
	 if(data->numCluster>0xFF){
		 memset(vecAux,0,4);
		 uint32_t nuevoTamanio=0;
		 nuevoTamanio =(swap_uint32(data->numCluster));
		 memcpy(vecAux,&nuevoTamanio,sizeof(uint32_t));
		 memcpy(buffer+offset+32+0x1A,vecAux,sizeof(uint16_t));
		 memcpy(buffer+offset+32+0x14,vecAux+2,sizeof(uint16_t));
	}
	 else{
		 memcpy(vecAux,&data->numCluster,sizeof(uint32_t));
		 memcpy(buffer+offset+32+0x1A,vecAux,sizeof(uint16_t));
		 memcpy(buffer+offset+32+0x14,vecAux+2,sizeof(uint16_t));
	 }

	 realizar_pedido_lectura(sector/2,buffer);
	 return 0;
 }

 /*____________________________________________________*/

 int borrarEntradaDir(char*rutaArchivo){
	 int borrado=0xE5;
  	
	fat32_directory *directorio,*ptr=	NULL;
	char*vecAux;
	directorio=malloc(sizeof(fat32_directory));
	int pos=0,tamanioPath=0,posAnt=0,i;
	directorio->tipo=0;
	
	for(i=0;rutaArchivo[i]!='.'&&i<strlen(rutaArchivo);i++);

	if(i<strlen(rutaArchivo))
		memcpy((rutaArchivo+i)," ",1);
	tamanioPath=strlen(rutaArchivo);

	pos=strcspn(rutaArchivo,"/");
	posAnt=pos+1;
	leerDirectorio(directorio,FAT32.numClusterDirRoot);
	pos=strcspn(rutaArchivo+posAnt,"/");

	vecAux=malloc(sizeof(char)*pos);
	strncpy(vecAux,rutaArchivo+posAnt,pos);
    pos+=posAnt;
	
    if(directorio->tipo==0)
    	return 3;
    ptr=directorio;
	while(ptr!=NULL&&(strncmp(ptr->nombre,vecAux,pos-1))!=0)
		ptr=ptr->sig;

	free(vecAux);

	if(ptr==NULL)
		return 1;

	else{
			while(pos<tamanioPath){
				posAnt=pos+1;

				if(ptr->tipo!=DIRECTORIO)
					return 1; //no puede haber un dir en un archivo

				free(directorio);
				directorio=malloc(sizeof(fat32_directory));
				directorio->tipo=0;
				leerDirectorio(directorio,ptr->numClusterInicial);

				pos=strcspn(rutaArchivo+posAnt,"/");
				if(pos>tamanioPath)
					return 1; //falla el parseo
				vecAux=malloc(pos);
				strncpy(vecAux,rutaArchivo+posAnt,pos);
				
				if(directorio->tipo==0)
					return 3;
				ptr=directorio;
				while(ptr!=NULL&&(0!=strncmp(ptr->nombre,vecAux,pos)))
					ptr=ptr->sig;

				if(ptr==NULL)
					return 1;

				pos+=posAnt;
				free(vecAux);
			}
			if(ptr->tipo==0)
				return 3;
  			char bufferEscritura[1024];

  			realizar_pedido_lectura(ptr->numSectorEntrada/2,bufferEscritura);
  			
  			memcpy(bufferEscritura+ptr->offsetEntrada,&borrado,sizeof(uint8_t));
  			realizar_pedido_escritura(ptr->numSectorEntrada/2,bufferEscritura);
  	}

  	return 0;
  }
